docs: Cloud-SDK Extraction Phase B/C/D plan + ADR 0036 (Configure RPC)#677
Conversation
8 PRs / 26 tasks across 4 repos (workflow + workflow-plugin-{aws,gcp,digitalocean}).
Builds on Phase A's merged IaCStateBackend contract + ListBackendNames RPC +
SDK serve hook + engine host-wiring. Phase B (AWS) + Phase D (DigitalOcean) share
the S3-compatible store; Phase C (GCP) gke contract gated on an interface-audit
spike (ADR 0036, Task 16).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ adversarial fixes Cycle-1 adversarial review FAIL + a grep-verified design gap (ADR 0035 lesson): the IaCStateBackend contract has no config-passing RPC, so Phase A's plugin-served backends are non-functional end-to-end. Add ADR 0036 (Configure RPC) and PRs 1-2 (host wiring + azure retrofit) as the prerequisite. Fold in cycle-1 fixes: aws S3 store drops DO env fallbacks; no in-core s3 case to delete; credential_source gap-window warning + coordinated-upgrade migration note; credentials_ref mechanism specified; minEngineVersion comparison verified before set; Option-3 PR9/PR8 serial note; parity-audit tasks folded. 8 PRs/24 tasks -> 10 PRs/29 tasks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Important: PR1->PR2 azure startup-gap-window co-deploy note in Task 4 CHANGELOG + Task 18 migration doc. Minor: ConfigureRequest/Response compile-guard line in Task 1; intentional-stdlib-log note in Task 15; explicit v1.0.0->v1.1.0 in Task 10; soft-fail rejected-alternative recorded in ADR 0036. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
This PR documents the next cloud-SDK extraction phases and records ADR 0036 for adding IaCStateBackend.Configure so plugin-served state backends can receive configuration.
Changes:
- Adds a locked Phase B/C/D implementation plan spanning workflow core and cloud provider plugins.
- Adds ADR 0036 for the
IaCStateBackend.ConfigureRPC and its operational tradeoffs. - Adds a scope-lock hash for the new plan.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md |
New multi-phase implementation plan for extracting AWS/GCP/DO SDK-backed surfaces into plugins. |
docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md.scope-lock |
Scope-lock hash for the new plan. |
decisions/0036-iac-state-backend-configure-rpc.md |
ADR accepting the new Configure RPC for plugin-served IaC state backend config plumbing. |
Comments suppressed due to low confidence (18)
docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:958
- This refers to
platform.kubernetesselectinggkeviaprovider, but the module actually reads the backend from config keytype(seemodule/platform_kubernetes.goand the schema'stypefield). If the migration guide follows this text, users will be told to set the wrong YAML key andgkeconfigs may continue to default tokind.
**Step 1: Write the Phase C migration section** — `iac.state backend: gcs` → load `workflow-plugin-gcp`; `platform.kubernetes provider: gke` → load `workflow-plugin-gcp` (`provider: kind|k3s|eks|aks` unchanged, still core); `storage.gcs` → load `workflow-plugin-gcp`, `credentials:` inline (or `credentials_ref:` a `gcp.credentials` module). yaml `backend:`/`provider:`/module-type names unchanged.
docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:874
- This implementation instruction uses
providerfor the Kubernetes backend selector, butPlatformKubernetes.Initreadsm.config["type"]and the schema exposestypeas the cluster selector. The task should describe dispatching ontype/cluster type; otherwise the registry wiring may be implemented or tested against a nonexistent config key.
**Context:** Structurally-identical to Phase A's `iac.state` plugin-backend wiring, for **kubernetes backends**: a `kubernetesBackendClientRegistry` (`gke` → contract client), an exported `RegisterKubernetesBackendClient`, a `plugin.KubernetesBackendProvider` optional interface, an `ExternalPluginAdapter` accessor, the `engine.go` `loadPluginInternal` seam. `module/platform_kubernetes.go` resolution: `provider: kind|k3s|eks|aks` use the in-core `kubernetesBackendRegistry` factory map unchanged; any other provider (`gke`) consult the new client registry and wrap the client in Task 25's `grpcKubernetesBackend`.
docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:567
- The rewrite drops data that the current
role_arnresolver consumes from the same credentials block: inline baseaccessKey/secretKey/sessionTokenandsessionName. After core only emitsRoleARN/external_id/credential_source, the plugin'sBuildAWSConfigcannot recreate the current AssumeRole behavior for configs that supply base static credentials or a custom session name, so this task should preserve those fields (for example inCloudCredentials/Extra) and test them.
`awsRoleARNResolver.Resolve` — keep the `credsMap` nil-check, the `roleARN`/`externalID` extraction, the `RoleARN` + `Extra["external_id"]` records, the `roleARN == ""` required-check; then:
```go
m.creds.Extra["credential_source"] = "role_arn"
logCredentialSourceMarker("aws", "role_arn")
return nil
}
(delete the sessionName extraction and the entire SDK block.) Add a small logCredentialSourceMarker(provider, source string) helper. The resolver call-site is a pure resolver with no module-scoped logger, so a stdlib log.Printf is the intentional pragmatic choice here — add a // TODO: plumb a structured logger when the resolver gains module context comment so it isn't mistaken for an oversight. Update the import block to "fmt" + "log" + "os" (drop context, aws, config, credentials, sts).
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:323**
* `CredInput` omits `SessionName`, even though the existing in-core `awsRoleARNResolver` reads `credentials.sessionName` and passes it to STS AssumeRole. Without carrying that field into the plugin credential API, the extraction silently changes AssumeRole behavior for users with a custom session name.
Step 3: Implement — internal/awscreds/awscreds.go: func BuildAWSConfig(ctx context.Context, creds CredInput) (aws.Config, error) where CredInput carries AccessKey/SecretKey/SessionToken/Region/RoleARN/ExternalID/Profile/Source. Logic: Source == "profile" → config.LoadDefaultConfig(ctx, config.WithSharedConfigProfile(profile)); Source == "role_arn" (or RoleARN != "") → port the deleted-from-core awsRoleARNResolver SDK block (base config + sts.NewFromConfig + AssumeRole), return a config carrying the assumed creds; static keys → config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(...)); else → config.LoadDefaultConfig(ctx). Wire the aws plugin's existing IaC-provider credential path to call BuildAWSConfig so a host-supplied CloudCredentials with a marker resolves in-plugin.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:352**
* Resolving `credentials_ref` from a process-local registry during the consumer factory call makes module order load-bearing: if a `storage.s3`/`step.s3_upload` module appears before the referenced `aws.credentials` module (or lacks an explicit `dependsOn` that causes topological reordering), the lookup fails even though the config is otherwise valid. The plan should either require/validate `dependsOn`, add a transform that injects it from `credentials_ref`, or defer resolution until module Init after all factories have run.
Context: storage.s3 becomes a plugin-native module via the existing ModuleFactories SDK path (no new contract). Credentials move inline per design §3 Option-1: a credentials: block resolved via awscreds.BuildAWSConfig, OR credentials_ref: an in-plugin aws.credentials module. credentials_ref: resolution mechanism (explicit per adversarial review): the aws plugin maintains a process-local credref registry (a map[string]CredInput guarded by a mutex, package internal/modules/credref); each aws.credentials module registers its resolved CredInput under its module name at factory-construction time; storage.s3/step.s3_upload factories look up credentials_ref: in that registry. credentials_ref: names must be unique within a config — duplicate registration is a factory error, not a silent clobber.
Step 1: Write the failing tests — storage_s3_test.go: factory builds the module from a config with an inline credentials: block AND from one with credentials_ref:. aws_credentials_test.go: the module registers its CredInput in the credref registry under its name; a second module with the same name → error.
Step 2: Verify they fail — cd /Users/jon/workspace/workflow-plugin-aws && go test ./internal/modules/... -v → FAIL undefined.
Step 3: Implement — internal/modules/credref/registry.go (the mutex-guarded map[string]CredInput + Register(name, CredInput) error rejecting duplicates + Resolve(name) (CredInput, bool)). Port module/s3_storage.go → internal/modules/storage_s3.go (package modules, resolve creds via awscreds.BuildAWSConfig from the inline block or the credref registry). internal/modules/aws_credentials.go — the aws.credentials module: parses a credentials: block into a CredInput, registers it in credref. Register "storage.s3" + "aws.credentials" in ModuleFactories; update plugin.json.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:793**
* This mirrors the AWS `credentials_ref` registry pattern, so it inherits the same ordering problem: resolving the ref in the `storage.gcs` factory only works if the `gcp.credentials` factory has already registered the name. Without an explicit dependency/transform or deferring lookup to Init, valid configs can fail solely because the credential module appears later in the YAML.
Context: storage.gcs becomes plugin-native, mirroring Task 8. The gcp credential resolvers (module/cloud_account_gcp.go) are already SDK-free, so gcpcreds.BuildGCPOptions builds []option.ClientOption from an inline credentials: block (ServiceAccountJSON → option.WithCredentialsJSON) with an ADC fallback. gcp.credentials + the credref registry mirror Task 8 exactly. This task also cuts the gcp plugin release (PR 10 is blocked on it).
Step 1: Write the failing tests — gcpcreds_test.go: BuildGCPOptions with inline service-account JSON; with empty input (ADC fallback). storage_gcs_test.go: factory from a config with credentials:/credentials_ref:. gcp_credentials_test.go: the module registers in credref by name; duplicate → error.
Step 2: Verify they fail — cd /Users/jon/workspace/workflow-plugin-gcp && go test ./internal/gcpcreds/ ./internal/modules/... -v → FAIL.
Step 3: Implement — internal/gcpcreds/gcpcreds.go (BuildGCPOptions); internal/modules/credref/registry.go (mirror Task 8); port module/storage_gcs.go → internal/modules/storage_gcs.go; internal/modules/gcp_credentials.go (the gcp.credentials DRY module). Register "storage.gcs" + "gcp.credentials" in ModuleFactories; extend host_conformance_test.go (capability parity for everything PR 8 added); update plugin.json (moduleTypes + a minor version bump); CHANGELOG entry naming gcs + the gke contract + storage.gcs/gcp.credentials.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:611**
* This deletion plan misses the hardcoded type/schema registries that still advertise these as core types. `storage.s3` is in `schema/schema.go`/`schema/module_schema.go`, `cmd/wfctl/type_registry.go`, `module/api_workflow_ui.go`, and manifest analyzers; `step.s3_upload` is also in `schema/step_schema_builtins.go`/`schema/module_schema.go`/`schema/schema.go`. If only the plugin factory maps and DOCUMENTATION are changed, validation/UI can still treat removed built-ins as available while the engine has no factory unless the AWS plugin is loaded.
- Modify:
plugins/storage/plugin.go(drop the"storage.s3"factory:89, the capability entry:37, the schema:326) - Modify:
plugins/pipelinesteps/plugin.go(drop the"step.s3_upload"factory:183, the capability entry:93) - Modify:
DOCUMENTATION.md(removestorage.s3/step.s3_uploadfrom the module/step tables)
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:900**
* The GCS deletion has the same stale-registry gap: `storage.gcs` is still listed in `schema/schema.go`, `schema/module_schema.go`, `cmd/wfctl/type_registry.go`, `module/api_workflow_ui.go`, manifest analyzers, and multiple docs. Removing only the storage plugin entry and DOCUMENTATION leaves core validators/UI advertising a built-in module type whose factory was deleted unless the GCP plugin is loaded.
- Delete:
module/iac_state_gcs.go,module/storage_gcs.go,module/platform_kubernetes_gke.go(+ their_test.goif present) - Modify:
module/iac_module.go(removecase "gcs":) - Modify:
plugins/storage/plugin.go(drop the"storage.gcs"factory:109, capability:39, schema:352) - Modify:
DOCUMENTATION.md(removestorage.gcs)
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:932**
* The planned `go list -deps` gate is broader than the files being removed and is likely unsatisfiable as written. Core still imports `golang.org/x/oauth2/google` in `provider/gcp/plugin.go` and `module/auth_oauth2.go`; that package brings Google metadata/cloud packages into the dependency graph in current `go.mod` (`cloud.google.com/go/compute/metadata` is already present). Deleting `storage.gcs`/`gke` may remove direct `cloud.google.com/go/storage` and `google.golang.org/api` importers, but it does not guarantee zero `cloud.google.com/go` packages in `go list -deps ./...`.
Context: After Task 27, cloud.google.com/go/storage + google.golang.org/api/* have zero importers in core's build graph — go mod tidy drops them entirely. The permanent CI gate is asymmetric (design Goals): (a) go list -deps ./... asserts zero Azure/azure-sdk-for-go AND zero cloud.google.com/go / google.golang.org/api packages in core's build graph; (b) audit-cloud-symbols.sh --check asserts zero aws-sdk-go-v2 imports under module/ — AWS gone from module/, but aws-sdk-go-v2 remains a go.mod entry for the out-of-scope provider/aws/ etc. surface. godo remains — not asserted.
Change class: Build pipeline + go.mod dependency change → runtime-launch-validation required. Rollback: revert PR 10; deleted files recoverable from git, the in-core gcs/storage.gcs/gke paths restore, go.mod re-adds the GCP SDKs on go mod tidy. Note: a running deployment that already cut over to plugin-served gcs must, on rollback, either also roll back the gcp plugin to a pre-gcs version OR keep the gcs-serving plugin installed (the reverted engine routes backend: gcs to the in-core case, so the in-core path must be the one in use) — coordinate engine + plugin versions.
Step 1: Tidy + marker — GOWORK=off go mod tidy && touch .phase-c-complete. Confirm go.mod no longer lists cloud.google.com/go/storage or google.golang.org/api.
Step 2: Add the permanent invariants — in scripts/audit-cloud-symbols.sh, add a --check block: GOWORK=off go list -deps ./... 2>/dev/null | grep -E 'Azure/azure-sdk-for-go|cloud\.google\.com/go|google\.golang\.org/api' must be empty (FAIL if any line). Add a module/-scoped aws-sdk-go-v2 zero-import assertion (the existing whole-repo map already separates module/ from elsewhere — assert the module/ count is 0). In .github/workflows/ci.yml cloud-sdk-audit job, confirm audit-cloud-symbols.sh --check runs (wired in Phase 0) and the new graph check executes there.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:260**
* Porting `iac_state_spaces.go` for the AWS `s3` backend needs to remove more than the `DO_SPACES_*` credential fallbacks. The current constructor also synthesizes a DigitalOcean endpoint (`https://<region>.digitaloceanspaces.com`) whenever `endpoint` is empty and forces `BaseEndpoint`/path-style; if that logic is copied into `NewS3IaCStateStore`, a normal AWS S3 backend with only `region`/`bucket` will target DigitalOcean instead of AWS. The plan should explicitly require AWS behavior: no synthesized DO endpoint, no custom `BaseEndpoint` unless configured, and path-style only when using a custom S3-compatible endpoint.
Step 3: Port the store — copy module/iac_state_spaces.go → internal/statebackend/s3.go. Edits:
package module→package statebackend.- Rename
SpacesIaCStateStore→S3IaCStateStore,NewSpacesIaCStateStore→NewS3IaCStateStore,NewSpacesIaCStateStoreWithClient→NewS3IaCStateStoreWithClient,SpacesS3Client→S3Client. - Strip the
DO_SPACES_ACCESS_KEY/DO_SPACES_SECRET_KEYenv-var fallbacks from the constructor — they are DigitalOcean-specific and would silently authenticate an awss3backend against DO credentials in a mixed deployment. Replace with the AWS-conventional behavior: ifaccessKey/secretKeyare empty, do not inject static creds — letaws-sdk-go-v2's default credential chain (envAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, instance role, etc.) apply viaconfig.LoadDefaultConfig. (The DO plugin's copy in Task 11 keeps theDO_SPACES_*fallbacks — that is correct there.) - Define a local
IaCStatestruct +IaCStateStoreinterface in this package. The struct fields must match the protoIaCStatemessage (iac.proto:636) exactly — the proto is the canonical wire shape; if the proto and core's Go struct ever diverge, the proto wins. The 6 method signatures are the ctx-fulmodule.IaCStateStoreshape. The plugin does NOT importworkflow/module.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:204**
* The test/input shape here uses `accountURL`, but the existing `azure_blob` YAML keys are snake_case (`account_url`, `account_name`, `account_key`, `container`, `prefix`) as documented in the Phase A migration guide and tests. If the retrofit decodes camelCase keys, existing `azure_blob` configs will still fail after `Configure`; the plan should require compatibility with the current snake_case keys and test those exact names.
Step 1: Write the failing test — internal/statebackend_server_test.go: call azureIaCServer.Configure(ctx, &pb.ConfigureRequest{BackendName: "azure_blob", ConfigJson: <json of {accountURL,container,...}>}); assert resolveStore() subsequently returns a non-nil store (not FailedPrecondition).
Step 2: Verify it fails — cd /Users/jon/workspace/workflow-plugin-azure && go test ./internal/ -run Configure -v → FAIL (Configure is the Unimplemented default → returns Unimplemented status).
Step 3: Implement — azureIaCServer.Configure: json.Unmarshal(req.ConfigJson, &cfg), validate req.BackendName == "azure_blob", construct statebackend.NewAzureBlobIaCStateStore(...) from the decoded config fields (account URL / container / credential — the same fields the deleted in-core iac_state_azure.go switch case read), call s.stateBackend.setStateStore(store), return &pb.ConfigureResponse{}. Pin workflow to PR 1's merge commit.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:652**
* This uses `credentialType`, but `cloud.account` reads the credential kind from `credentials.type` (defaulting to `static`) rather than a top-level `credentialType` key. The migration guide should name the actual YAML shape (`provider: aws` with `credentials.type: profile|role_arn`) so users can identify the affected configs.
provider: awswithcredentialType: profileorrole_arn— credential resolution is now performed in-plugin. Core andworkflow-plugin-awsmust be upgraded together: a new core against a pre-extraction aws plugin will emit acredential_sourcemarker the old plugin ignores, producing empty credentials (core logs a warning). State this prominently.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:260**
* This should also require the local `IaCState` JSON tags to match the existing core `module.IaCState` at-rest format (`resource_id`, `resource_type`, etc.). The plan explicitly keeps the at-rest JSON format out of scope, so defining the struct only from the proto shape (or omitting the core JSON tags) would make the plugin unable to read state objects previously written by the in-core S3-compatible store.
- Define a local
IaCStatestruct +IaCStateStoreinterface in this package. The struct fields must match the protoIaCStatemessage (iac.proto:636) exactly — the proto is the canonical wire shape; if the proto and core's Go struct ever diverge, the proto wins. The 6 method signatures are the ctx-fulmodule.IaCStateStoreshape. The plugin does NOT importworkflow/module.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:432**
* Because the in-core `spaces` backend has existing JSON state objects, the copied plugin struct must preserve the core `module.IaCState` JSON tags in addition to matching the proto fields. Otherwise the DO plugin can compile and pass wire-shape tests while failing to read state written before the extraction, which violates the plan's out-of-scope at-rest-format change.
Step 3: Implement — copy module/iac_state_spaces.go → internal/statebackend/spaces.go (package statebackend; keep the Spaces* names and the DO_SPACES_* env fallbacks; define a local IaCState struct matching the proto IaCState message + the IaCStateStore interface; do NOT import workflow/module). go mod tidy.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:705**
* The GCS port also needs an explicit at-rest compatibility requirement: the local `IaCState` struct should preserve the current `module.IaCState` JSON tags, not just match the proto fields. Existing GCS state files are JSON written with the core tags, and the plan says the at-rest format change is out of scope.
Step 3: Implement — copy module/iac_state_gcs.go → internal/statebackend/gcs.go (package statebackend, local IaCState + IaCStateStore, keep the GCSObjectClient indirection + gcsRealClient, do NOT import workflow/module). go mod tidy.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:991**
* This note says the AWS and DO copies differ only in env-var fallbacks, but the AWS `s3` copy also must not inherit the DO-specific endpoint synthesis/path-style behavior from `SpacesIaCStateStore`. Keeping the note as-is encourages future changes to preserve an AWS bug (routing `s3` to `*.digitaloceanspaces.com`) or to reintroduce it when syncing the two copies.
S3IaCStateStore(aws) andSpacesIaCStateStore(DO) are deliberately diverging copies of the same upstreammodule/iac_state_spaces.go— they differ only in env-var fallback behavior (aws strips theDO_SPACES_*fallbacks; DO keeps them). Any future fix to the shared S3-locking protocol must be applied to both copies. The design's "Alternative 3 — shareds3compatmodule" is the recommended eventual cleanup; out of scope here.
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:847**
* The core adapter instructions do not specify how the plugin receives the `cloud.account` credentials that `gkeBackend.containerService` currently reads from `k.provider.GetCredentials()`. Task 22 assumes credentials arrive as serialized `CloudCredentials`, but the Task 25 ResourceDriver/adapter mapping only mentions plan/result/state JSON conversion. Without explicitly fetching `k.provider.GetCredentials(ctx)` and embedding the credentials/project/region in the ResourceDriver request config, the plugin-served GKE path cannot authenticate.
Context: The host-side adapter that lets platform.kubernetes's in-core kubernetesBackend interface dispatch the gke provider to a plugin gRPC client. Shape per ADR 0037:
- Option 1 (ResourceDriver fold):
grpcKubernetesBackendimplementskubernetesBackend, delegatingplan→Diff,apply→Create/Update,status→Read,destroy→Deleteon apb.ResourceDriverClient. JSON-bytes converters (PlatformPlan/PlatformResult/KubernetesClusterState↔ theResourceDrivermessages), mirroringiac_state_grpc_client.go. No proto change. - Option 2: the
RemoteModuleadapter for a plugin-nativekubernetesBackend. - Option 3: add the minimal
PlatformBackendservice toiac.proto(regenerate — additive, preserves the no-structpbinvariant) + thegrpcKubernetesBackendadapter over it. If Option 3, this task's proto regen must merge before PR 8's Task 22.
Step 1: Read ADR 0037. Pin the contract.
Step 2: Write the failing test — platform_kubernetes_grpc_test.go: a fake client of the chosen contract; assert grpcKubernetesBackend.{plan,apply,status,destroy} round-trip (incl. KubernetesClusterState surviving the JSON-bytes round-trip).
**docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md:675**
* The interface-audit spike needs to check more than lifecycle/status shape: GKE `status` and `destroy` currently need project/location and GCP credentials from `PlatformKubernetes`/`cloud.account`, but `ResourceReadRequest` and `ResourceDeleteRequest` carry only `ResourceRef` (no `config_json` or credentials). If ADR 0037 chooses the ResourceDriver fold without solving that, the plugin will not have enough data to implement `status`/`destroy` unless it relies on some separate provider initialization path.
Step 1: Audit the in-core interface — read module/platform_kubernetes.go, module/platform_kubernetes_gke.go (the gkeBackend 4 methods + containerService), module/platform_provider.go (PlatformPlan/PlatformResult), module/platform_kubernetes.go:11 (KubernetesClusterState), and plugin/external/proto/iac.proto (ResourceDriver + its messages). Map each kubernetesBackend method onto a ResourceDriver RPC; note any shape mismatch (status returns the rich typed KubernetesClusterState — does ResourceReadResponse.outputs_json carry it cleanly? — and confirm gke has no continuous-reconciliation behavior: the 4 methods are one-shot lifecycle).
</details>
| **Step 5: Commit** | ||
|
|
||
| ```bash | ||
| git add plugin/external/proto/ module/iac_state_grpc_client.go module/iac_state_grpc_client_test.go |
There was a problem hiding this comment.
Valid systemic nit — acknowledged. The plan is scope-locked (the git add lines sit inside task descriptions, but the lock hook blocks all plan edits and this isn't a scope change, so the heavyweight unlock path isn't warranted). Mitigated instead at the execution layer: every implementer has been briefed that the git add lines are illustrative — always git status and stage every created/modified file (test fakes for regenerated interfaces, generated .pb.go, go.sum). Empirically confirmed on Task 1, which staged all 7 files (proto + regen + client + 2 fakes + test + compile-guard) correctly.
⏱ Benchmark Results✅ No significant performance regressions detected. benchstat comparison (baseline → PR)
|
Summary
docs/plans/2026-05-14-cloud-sdk-extraction-bcd.md) — 10 PRs / 29 tasks acrossworkflow+workflow-plugin-{azure,aws,gcp,digitalocean}.decisions/0036— adds theIaCStateBackend.ConfigureRPC: Phase A's contract shipped with no config-passing RPC, leaving plugin-served IaC state backends non-functional end-to-end. PRs 1–2 of the B/C/D plan close that gap.Docs/decisions only — no code. This lands the plan + ADR on
mainso the 10 execution PRs (each offmain) can referencedecisions/0036.Test plan
plan-scope-check.shPASS (manifest well-formed, 10 PRs / 29 tasks)🤖 Generated with Claude Code